perm filename GOBBLE.SAI[OLD,HE] blob sn#501000 filedate 1980-03-25 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00018 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	IFCR ¬DECLARATION(EXTENDED_COMPILATION) THENC
C00004 00003	! CHANNEL STUFF: readfile
C00005 00004	!  Definitions
C00006 00005	!  rwdo, rwmake, dirmake, codemake, dtypmake, inpinit
C00014 00006	!  nextline, inscan, skipblanks, scan_token
C00018 00007	!  read and fread
C00020 00008	!  get_dtype, verify_dtype, verify_1, verify_2, verify_3, dtype_check
C00031 00009	!  asgbki, identlookup, ensym, vblmake, vtry
C00036 00010	!  grovel (lllop, gllop, stgrovel, lgrovel, constelim)
C00039 00011	!  grovel: REGROVEL:  DIR, EOP, DTYP
C00042 00012	!  grovel: DTYP: ARRAY, PROCEDURE
C00048 00013	!  grovel: main body:	PROG,BLOCK,COBLOCK,FORR,WHIL,IFF,PAUSE,ABORT,COMMNT
C00052 00014	!  grovel: main body:	CASE, RETURN
C00056 00015	!  grovel: main body:	DEPROACH, PAS, PVL, NOTE, NOTE1, NOTE2
C00058 00016	!  grovel: main body:	AFFIX, UNFIX
C00060 00017	!  grovel: main body:	V3ECT, TRANS, ASSIGNMENT, EVDO, PRNT, CMABLE
C00064 00018	!  grovel: main body:   MOVE$, OPERATE, CENTER, STOP, motion clauses
C00080 ENDMK
C⊗;
IFCR ¬DECLARATION(EXTENDED_COMPILATION) THENC

ENTRY;

BEGIN  "GOBBLE"
IFCR ¬DECLARATION(CREFFING) THENC DEFINE CREFFING="FALSE"; ENDC
IFCR ¬ CREFFING THENC
REQUIRE "ALREQ.HDR[AL,HE]" SOURCE_FILE;
ENDC
REDEFINE $$PRGID "[]" = ["GOBBLE"];
ENDC

RCLASS RESWD(STRING NAME; INTEGER TYPE, CODE; RPTR(RESWD) NEXT);
RCLASS IDENT(STRING ID; RPTR(IDENT) NEXT);
INTERNAL RCLASS DEFID(STRING NAME; RANY VAL; RPTR(DEFID) NEXT);

RPTR(RESWD) ARRAY BUCKET[1:26];
INTERNAL RPTR(DEFID) SYSIDS;
RPTR(DEFID) IDS;
RPTR(IDENT) IDENTS;

DEFINE DSKIN_OP = 1;

! CHANNEL STUFF: readfile;
DEFINE MAXFILES="15";	! This is all an old relic, but why bother changing it;
STRING ARRAY FID[0:MAXFILES];
INTEGER ARRAY EOF[0:MAXFILES];
INTEGER ARRAY BRCHAR[0:MAXFILES];

INTEGER PROCEDURE READFILE(STRING FILEID;INTEGER DMODE(0));
    BEGIN
    INTEGER CH;
    CH←GETCHAN;
    FID[CH]←FILEID;
    OPEN(CH,"DSK",DMODE,3,0,512,BRCHAR[CH],EOF[CH]);
    LOOKUP(CH,FILEID,EOF[CH]);
    IF EOF[CH] THEN
	BEGIN
	USERERR(1,1,"LOOKUP FAILED FOR |"&FILEID&"|");
	RELEASE(CH);
	CH←-1;
	END;
    RETURN(CH);
    END;

!  Definitions;

DEFINE MAXINPLEV=3;
INTEGER ARRAY SCNCHN[1:MAXINPLEV];
STRING ARRAY SCNSTK[0:MAXINPLEV];
INTEGER INPLEV;

RANY SYM;

INTEGER LINBRK,BLNKBRK,IDBRK,STRBRK;

DEFINE	UNKN_CODE = 0;		! Unknown code;
DEFINE	IDENT_CODE = 1;		! identifier;
DEFINE	RW_CODE = 2;		! Reserved word;
DEFINE	VAL_CODE = 3;		! Scalar value;
DEFINE	STR_CODE = 4;		! String constant;
DEFINE	DIR_CODE = 5;		! Directive (DSKIN);
DEFINE	EOP_CODE = 6;		! Expression operation (SADD ...);
DEFINE	DTYP_CODE = 7;		! Declaration (SVAR ...);
DEFINE	PREDEC_CODE = 8;	! Predeclared variable/constant (BARM, XHAT...);
!  rwdo, rwmake, dirmake, codemake, dtypmake, inpinit;

PROCEDURE RWDO(STRING ID;INTEGER TYPE,I);
    BEGIN
    INTEGER B;
    RPTR(RESWD) V;
    V ← NEW_RECORD(RESWD);
    RESWD:NAME[V] ← ID;
    RESWD:TYPE[V] ← TYPE;
    RESWD:CODE[V] ← I;
    B ← ID - '100;	! Use first character as index for proper bucket;
    RESWD:NEXT[V] ← BUCKET[B];
    BUCKET[B] ← V
    END;

PROCEDURE RWMAKE(STRING ID;INTEGER I);
    RWDO(ID,RW_CODE,I);

PROCEDURE DIRMAKE(STRING ID;INTEGER I);
    RWDO(ID,DIR_CODE,I);

PROCEDURE CODEMAKE(STRING ID;INTEGER I);
    RWDO(ID,EOP_CODE,I);

PROCEDURE DTYPMAKE(STRING ID;INTEGER I);
    RWDO(ID,DTYP_CODE,I);

PROCEDURE INPINIT;
    BEGIN
    SETBREAK(LINBRK←GETBREAK,LF,CR,"INS"); ! line break;
    SETBREAK(BLNKBRK←GETBREAK," "&'14&TAB&CR&LF,NULL,"XRN");
    SETBREAK(IDBRK←GETBREAK,"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$",NULL,"KXRN");
    SETBREAK(STRBRK←GETBREAK,""""&LF,CR,"INS");
    INPLEV←0;
    DIRMAKE("DSKIN",DSKIN_OP);
    RWMAKE("NULL",0);
    RWMAKE("AFFIX",AFFIXTYPE);
    RWMAKE("COMMENT",COMMNTTYPE);
    RWMAKE("ON",CMONTYPE);
    RWMAKE("EV",EVDOTYPE);
    RWMAKE("CMABLE",CMABLETYPE);
    RWMAKE("UNFIX",UNFIXTYPE);
    RWMAKE("PR",PROGTYPE);
    RWMAKE("BL",BLOCKTYPE);
    RWMAKE("CO",COBLOCKTYPE);
    RWMAKE("FO",FORRTYPE);
    RWMAKE("WH",WHILTYPE);
    RWMAKE("UNTL",UNTLTYPE);
    RWMAKE("CASE",KASETYPE);
    RWMAKE("IF",IFFTYPE);
    RWMAKE("PAUSE",PAUSETYPE);
    RWMAKE("PROMPT",PROMPTTYPE);
    RWMAKE("ABORT",ABORTTYPE);
    RWMAKE("RET",RETRNTYPE);
    RWMAKE("AS",ASSIGNMENTTYPE);
    RWMAKE("PAS",PASTYPE);
    RWMAKE("DEPROACH",DEPROACHTYPE);
    RWMAKE("MO",MOVE$TYPE);
    RWMAKE("TO",TOTYPE);
    RWMAKE("OPERATE",OPERATETYPE);
    RWMAKE("CENTER",CENTERTYPE);
    RWMAKE("ERROR",ERRORTYPE);
    RWMAKE("RETRY",RETRYTYPE);
    RWMAKE("STOP",STOPTYPE);
    RWMAKE("DURATION",DURATIONTYPE);
    RWMAKE("FORCE",FORCETYPE);
    RWMAKE("STIFFNESS",STIFFTYPE);
    RWMAKE("GATHER",GATHERTYPE);
    RWMAKE("FORCE_FRAME",F_FRAMETYPE);
    RWMAKE("SETBASE",SETBASETYPE); ! This and WRIST are temp hacks for JKS;
    RWMAKE("WRIST",WRISTTYPE);	    ! so he can debug the force wrist;
    RWMAKE("PRINT",PRNTTYPE);
    RWMAKE("VIA",VIATYPE);
    RWMAKE("VELOCITY",VELOCITYTYPE);
    RWMAKE("ARRIVAL",APPROACHTYPE);
    RWMAKE("DEPARTURE",DEPARTURETYPE);
    RWMAKE("OPENING",OPENINGTYPE);
    RWMAKE("WOBBLE",WOBBLETYPE);
    RWMAKE("SPEED_FACTOR",S_FACTYPE);
    RWMAKE("NNULL",NNULLTYPE);
    RWMAKE("RTMOVE",RTMOVETYPE);
    RWMAKE("SW_TIME",SW_TIMETYPE); ! for vise;
    RWMAKE("CW",CWTYPE); ! for driver;
    RWMAKE("PVL",PVLTYPE);
    RWMAKE("NOTE",NOTETYPE);
    RWMAKE("NOTE1",NOTE1TYPE);
    RWMAKE("NOTE2",NOTE2TYPE);
    RWMAKE("DEBUG",DEBUGTYPE);	! for debugging GROVEL;
    CODEMAKE("NOOP",NO_OP);
    CODEMAKE("CALL",CALL_OP);
    CODEMAKE("AREF",AREF_OP);
    CODEMAKE("SSBRTN",SSBRTN_OP);
    CODEMAKE("SCALRD",SCALRD_OP);
    CODEMAKE("SABS",SABS_OP);
    CODEMAKE("SADD",SADD_OP);
    CODEMAKE("SSUB",SSUB_OP);
    CODEMAKE("SMUL",SMUL_OP);
    CODEMAKE("SNEG",SNEG_OP);
    CODEMAKE("SDIV",SDIV_OP);
    CODEMAKE("STOS",SEXP_OP);
    CODEMAKE("MAX",MAX_OP);
    CODEMAKE("MIN",MIN_OP);
    CODEMAKE("INT",INT_OP);
    CODEMAKE("DIV",DIV_OP);
    CODEMAKE("MOD",MOD_OP);
    CODEMAKE("QUERY",QUERY_OP);
    CODEMAKE("SLT",SLT_OP);
    CODEMAKE("SEQ",SEQ_OP);
    CODEMAKE("SLE",SLE_OP);
    CODEMAKE("SGE",SGE_OP);
    CODEMAKE("SNE",SNE_OP);
    CODEMAKE("SGT",SGT_OP);
    CODEMAKE("AND",AND_OP);
    CODEMAKE("OR",OR_OP);
    CODEMAKE("NOT",NOT_OP);
    CODEMAKE("XOR",XOR_OP);
    CODEMAKE("EQV",EQV_OP);
    CODEMAKE("VMAGN",VMAGN_OP);
    CODEMAKE("VDOT",VDOT_OP);
    CODEMAKE("VMAKE",VMAKE_OP);
    CODEMAKE("SVMUL",SVMUL_OP);
    CODEMAKE("VSDIV",VSDIV_OP);
    CODEMAKE("VADD",VADD_OP);
    CODEMAKE("VSUB",VSUB_OP);
    CODEMAKE("VCROSS",VCROSS_OP);
    CODEMAKE("RVMUL",RVMUL_OP);
    CODEMAKE("TVMUL",TVMUL_OP);
    CODEMAKE("AXIS",AXIS_OP);
    CODEMAKE("RMAGN",RMAGN_OP);
    CODEMAKE("UVECT",UVECT_OP);
    CODEMAKE("POS",POS_OP);
    CODEMAKE("ORIENT",ORIENT_OP);
    CODEMAKE("RRMUL",RRMUL_OP);
    CODEMAKE("AXW_ROTN",AXW_ROTN_OP);
    CODEMAKE("TMAKE",TMAKE_OP);
    CODEMAKE("CONSTR",CONSTR_OP);
    CODEMAKE("FTOF",FTOF_OP);
    CODEMAKE("TVADD",TVADD_OP);
    CODEMAKE("TVSUB",TVSUB_OP);
    CODEMAKE("TTMUL",TTMUL_OP);
    CODEMAKE("TINVRT",TINVRT_OP);
    CODEMAKE("DEPR",DEPR_OP);
    CODEMAKE("FMAKE",FMAKE_OP);
    DTYPMAKE("REF",REF_DTYPE);
    DTYPMAKE("VAL",VAL_DTYPE);
    DTYPMAKE("SVAR",SVAL_DTYPE);
    DTYPMAKE("VVAR",V3ECT_DTYPE);
    DTYPMAKE("TVAR",TRANS_DTYPE);
    DTYPMAKE("RVAR",ROTN_DTYPE);
    DTYPMAKE("FVAR",FRAME_DTYPE);
    DTYPMAKE("EVAR",EVENT_DTYPE);
    DTYPMAKE("ARAY",ARAY_DTYPE);
    DTYPMAKE("PROC",PROC_DTYPE);

    DTYPMAKE("LAB",STMLAB_DTYPE);
    DTYPMAKE("OMNLAB",OMNLAB_DTYPE);
    DTYPMAKE("STMLAB",STMLAB_DTYPE);

    END;

REQUIRE INPINIT INITIALIZATION [2];
!  nextline, inscan, skipblanks, scan_token;

PROCEDURE NEXTLINE;
    BEGIN
    WHILE INPLEV>0 DO
	BEGIN
	IF ¬EOF[SCNCHN[INPLEV]] THEN
	    BEGIN
	    SCNSTK[INPLEV]←SCNSTK[INPLEV] & INPUT(SCNCHN[INPLEV],LINBRK);
	    RETURN;
	    END
	ELSE
	    BEGIN
	    RELEASE(SCNCHN[INPLEV]);
	    INPLEV←INPLEV-1;
	    END;
	END;
    OUTSTR("*");
    SCNSTK[0]←SCNSTK[0]&INCHWL&LF;
    END;

STRING PROCEDURE INSCAN(INTEGER BRKTBL;REFERENCE INTEGER BC);
    BEGIN
    WHILE ¬LENGTH(SCNSTK[INPLEV]) DO NEXTLINE;
    RETURN(SCAN(SCNSTK[INPLEV],BRKTBL,BC));
    END;

INTEGER PROCEDURE SKIPBLANKS;
    BEGIN
    ! returns the first non-"blank" character;
    INTEGER C;
    STRING S;
    DO S←INSCAN(BLNKBRK,C) UNTIL C≠0;
    RETURN(C);
    END;

INTEGER PROCEDURE SCAN_TOKEN;
    BEGIN
    RANY R;
    STRING SCNID;
    INTEGER C,IX;
    C ← SKIPBLANKS;
    IF C = "$" THEN			! A reserved word;
	BEGIN
	SCNID ← INSCAN(IDBRK,C);
	C ← LOP(SCNID);			! Ignore the $;
	C ← SCNID - '100;		! Which bucket to check;
	R ← BUCKET[C];
	WHILE R ≠ RNULL ∧ ¬EQU(SCNID,RESWD:NAME[R]) DO R ← RESWD:NEXT[R];
	IF R = RNULL THEN USERERR(1,1,"GOBBLE: UNKNOWN RESERVED WORD!");
	SYM ← R;
	RETURN(-1)
	END;

    IF "A" ≤(C LAND '137)≤ "Z" ∨ C="_" THEN	! an identifier;
	BEGIN
	INTEGER TYP;
	SCNID←INSCAN(IDBRK,C);
	R ← SYSIDS;
	WHILE R ≠ RNULL ∧ ¬EQU(SCNID,DEFID:NAME[R]) DO R ← DEFID:NEXT[R];
	IF R ≠ RNULL THEN
	    BEGIN
	    SYM ← DEFID:VAL[R];		! Found it, return value;
	    RETURN(-1)
	    END;
	R ← IDENTS;
	WHILE R ≠ RNULL ∧ ¬EQU(SCNID,IDENT:ID[R]) DO R ← IDENT:NEXT[R];
	IF R = RNULL THEN
	    BEGIN			! New - have to declare it now;
	    R ← NEW_RECORD(IDENT);
	    IDENT:ID[R] ← SCNID;
	    IDENT:NEXT[R] ← IDENTS;
	    IDENTS ← R
	    END;
	SYM ← R;
	RETURN(-1)
	END;

    IX ← IF C="-" ∨ C="+" THEN 2 ELSE 1;
    IF SCNSTK[INPLEV][IX FOR 1]="." THEN IX ← IX+1;
    IF "0"≤SCNSTK[INPLEV][IX FOR 1]≤"9" THEN
	BEGIN
	SYM ← NEW_RECORD(SVAL);
	SVAL:VAL[SYM] ← REALSCAN(SCNSTK[INPLEV],C);
	RETURN(-1)
	END;
    IF C="""" THEN
	BEGIN
	SCNID ← NULL;
	WHILE TRUE DO
	    BEGIN
	    C ← LOP(SCNSTK[INPLEV]);
	    SCNID ← SCNID & INSCAN(STRBRK,C);
	    IF C="""" THEN
		IF SCNSTK[INPLEV]="""" THEN SCNID←SCNID&LOP(SCNSTK[INPLEV])
		ELSE DONE
	    ELSE IF C=LF ∨ C=0 THEN SCNID ← SCNID & CRLF
	    END;
	IF SCNID = NULL THEN SCNID ← CRLF;
	SYM ← NEW_RECORD(STCONST);
	STCONST:VAL[SYM] ← SCNID;
	RETURN(-1)
	END;

    C ← LOP(SCNSTK[INPLEV]);
    RETURN(C)
    END;
!  read and fread;

INTERNAL RANY RECURSIVE PROCEDURE READ(INTEGER T(0));
    BEGIN
    RCELL LD;
    RCELL C;
    RANY V;

    IF T=0 THEN T←SCAN_TOKEN;

    IF T < 0 THEN RETURN(SYM);

    IF T="(" THEN
	BEGIN
	LD ← C ← RNULL;
	WHILE (T←SCAN_TOKEN) ≠ ")" DO
	    BEGIN
	    V ← CONS(READ(T),RNULL);
	    IF LD = RNULL THEN LD ← V ELSE CELL:CDR[C] ← V;
	    C ← V
	    END;
	RETURN(LD)
	END;

    V ← NEW_RECORD(CHAR_REC);
    CHAR_REC:CHAR[V] ← T;
    RETURN(V)
    END;

INTERNAL RANY PROCEDURE FREAD(STRING FILE_NAME);
    BEGIN   ! hack for linking with the parser and/or snail in rpg mode;
    SCNSTK[0] ← "($DSKIN """&FILE_NAME&""") ";
    RETURN(READ)
    END;
!  get_dtype, verify_dtype, verify_1, verify_2, verify_3, dtype_check;

FORWARD RPTR(VARIABLE) PROCEDURE VTRY
    (RANY V;INTEGER DTYP (INVALID_DTYPE));
    ! On the next page;

INTEGER PROCEDURE GET_DTYPE(RANY X; INTEGER DTYP (INVALID_DTYPE));
    BEGIN
    !  If X is a variable, VTRY is called on it with DTYP.
    This helps in properly declaring undeclared variables
    which are first used in expressions;
    INTEGER I;
    I ← RECTYPE(X);
    RETURN ( IF I = LOC(EXPRN) THEN EXPRN:DATATYPE[X]
	ELSE IF I = LOC(LBLVAR) THEN LBLVAR:DATATYPE[X]
	ELSE IF I = LOC(VARIABLE) THEN VARIABLE:DATATYPE[VTRY(X,DTYP)]
	ELSE IF I = LOC(ARRAYDEF) THEN ARRAYDEF:DATATYPE[X]
	ELSE IF I = LOC(SVAL) THEN SVAL_DTYPE
	ELSE IF I = LOC(V3ECT) THEN V3ECT_DTYPE
	ELSE IF I = LOC(ROTN) THEN ROTN_DTYPE
	ELSE IF I = LOC(TRANS) THEN TRANS_DTYPE
	ELSE IF I = LOC(FRAME) THEN FRAME_DTYPE
	ELSE INVALID_DTYPE)
    END;

PROCEDURE VERIFY_DTYPE(RPTR(EXPRN,VARIABLE,VALU$) X;INTEGER T);
    BEGIN
    INTEGER TT;
    TT ← GET_DTYPE(X,T);
    IF TT≠T THEN
	BEGIN
	IF ¬(TT = FRAME_DTYPE ∧ T = TRANS_DTYPE) THEN
	    BEGIN
	    ALPRIN(X);
	    USERERR(1,1,"PARSER: wrong expression data type");
	    END
	END
    END;

PROCEDURE VERIFY_1(RCELL C;INTEGER T);
    IF C=NULL THEN USERERR(1,1,"NOT ENOUGH ARGS")
	      ELSE VERIFY_DTYPE(CELL:CAR[C],T);

PROCEDURE VERIFY_2(RCELL C;INTEGER T1,T2);
    IF CL_LEN(C) < 2 THEN USERERR(1,1,"NOT ENOUGH ARGS")
	ELSE
	    BEGIN
	    VERIFY_DTYPE(CELL:CAR[C],T1);
	    VERIFY_DTYPE(CELL:CAR[CELL:CDR[C]],T2)
	    END;

PROCEDURE VERIFY_3(RCELL C;INTEGER T1,T2,T3);
    IF C=NULL THEN USERERR(1,1,"NOT ENOUGH ARGS")
	ELSE
	    BEGIN
	    VERIFY_DTYPE(CELL:CAR[C],T1);
	    VERIFY_2(CELL:CDR[C],T2,T3)
	    END;

PROCEDURE DTYPE_CHECK(RPTR(EXPRN) E);
    BEGIN
    INTEGER OP,NARGS;
    RCELL EARGS,C,T;
    RANY P;

    OP ← EXPRN:OP[E];
    EARGS ← EXPRN:ARGS[E];

    EXPRN:DATATYPE[E] ←
	IF OP = AREF_OP THEN ARRAYDEF:DATATYPE[P←LLOP(EARGS)]
	ELSE IF OP = CALL_OP THEN PROCDEF:DATATYPE[P←LLOP(EARGS)]
	ELSE IF MIN_SVAL_OP ≤ OP ≤ MAX_SVAL_OP THEN SVAL_DTYPE
	ELSE IF MIN_V3ECT_OP ≤ OP ≤ MAX_V3ECT_OP THEN V3ECT_DTYPE
	ELSE IF MIN_ROTN_OP ≤ OP ≤ MAX_ROTN_OP THEN ROTN_DTYPE
	ELSE IF MIN_TRANS_OP ≤ OP ≤ MAX_TRANS_OP THEN TRANS_DTYPE
	ELSE IF MIN_FRAME_OP ≤ OP ≤ MAX_FRAME_OP THEN FRAME_DTYPE
	ELSE INVALID_DTYPE;

    CASE OP OF BEGIN

[SCALRD_OP] [QUERY_OP] ; ! don't have any args;
[SABS_OP]	VERIFY_1(EARGS,SVAL_DTYPE);
[SADD_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[SSUB_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[SNEG_OP]	VERIFY_1(EARGS,SVAL_DTYPE);
[SMUL_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[SDIV_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[SEXP_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[MAX_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[MIN_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[INT_OP]	VERIFY_1(EARGS,SVAL_DTYPE);
[DIV_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[MOD_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[SLT_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[SGT_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[SEQ_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[SLE_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[SGE_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[SNE_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[AND_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[OR_OP]		VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[NOT_OP]	VERIFY_1(EARGS,SVAL_DTYPE);
[XOR_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[EQV_OP]	VERIFY_2(EARGS,SVAL_DTYPE,SVAL_DTYPE);
[VMAGN_OP]	VERIFY_1(EARGS,V3ECT_DTYPE);
[VDOT_OP]	VERIFY_2(EARGS,V3ECT_DTYPE,V3ECT_DTYPE);
[SVMUL_OP]	VERIFY_2(EARGS,SVAL_DTYPE,V3ECT_DTYPE);
[VSDIV_OP]	VERIFY_2(EARGS,V3ECT_DTYPE,SVAL_DTYPE);
[VMAKE_OP]	VERIFY_3(EARGS,SVAL_DTYPE,SVAL_DTYPE,SVAL_DTYPE);
[VADD_OP]	VERIFY_2(EARGS,V3ECT_DTYPE,V3ECT_DTYPE);
[VSUB_OP]	VERIFY_2(EARGS,V3ECT_DTYPE,V3ECT_DTYPE);
[VCROSS_OP]	VERIFY_2(EARGS,V3ECT_DTYPE,V3ECT_DTYPE);
[TVMUL_OP]	VERIFY_2(EARGS,TRANS_DTYPE,V3ECT_DTYPE);
[TVADD_OP]	VERIFY_2(EARGS,TRANS_DTYPE,V3ECT_DTYPE);
[TVSUB_OP]	VERIFY_2(EARGS,TRANS_DTYPE,V3ECT_DTYPE);
[RVMUL_OP]	VERIFY_2(EARGS,ROTN_DTYPE,V3ECT_DTYPE);
[RMAGN_OP]	VERIFY_1(EARGS,ROTN_DTYPE);
[AXIS_OP]	VERIFY_1(EARGS,ROTN_DTYPE);
[POS_OP]	VERIFY_1(EARGS,TRANS_DTYPE);
[ORIENT_OP]	VERIFY_1(EARGS,TRANS_DTYPE);
[RRMUL_OP]	VERIFY_2(EARGS,ROTN_DTYPE,ROTN_DTYPE);
[UVECT_OP]	VERIFY_1(EARGS,V3ECT_DTYPE);
[AXW_ROTN_OP]	VERIFY_2(EARGS,V3ECT_DTYPE,SVAL_DTYPE);
[FTOF_OP]	VERIFY_2(EARGS,FRAME_DTYPE,FRAME_DTYPE);
[TMAKE_OP]	VERIFY_2(EARGS,ROTN_DTYPE,V3ECT_DTYPE);
[CONSTR_OP]	VERIFY_3(EARGS,V3ECT_DTYPE,V3ECT_DTYPE,V3ECT_DTYPE);
[TTMUL_OP]	VERIFY_2(EARGS,TRANS_DTYPE,TRANS_DTYPE);
[TINVRT_OP]	VERIFY_1(EARGS,TRANS_DTYPE);
[DEPR_OP]	VERIFY_1(EARGS,FRAME_DTYPE);
[FMAKE_OP]	VERIFY_2(EARGS,ROTN_DTYPE,V3ECT_DTYPE);
[SSBRTN_OP]	CASE (OP ← SVAL:VAL[CELL:CAR[EARGS]]) OF
		  BEGIN
    [SQRT_OP] [SIN_OP] [COS_OP] [TAN_OP]
    [ASIN_OP] [ACOS_OP]
    [LOG_OP] [EXP_OP]
    [TIME_OP]	    VERIFY_1(CELL:CDR[EARGS],SVAL_DTYPE);
    [ATAN2_OP]	    VERIFY_2(CELL:CDR[EARGS],SVAL_DTYPE,SVAL_DTYPE)
		  END;
[CALL_OP]	BEGIN "procedure call"
		  NARGS ← 0;
		  T ← PROCDEF:ARGS[P];
		  WHILE EARGS ≠ RNULL DO
		    BEGIN "count args"
			NARGS ← NARGS + 1;
			VERIFY_DTYPE((C←LLOP(EARGS)),VARIABLE:DATATYPE[LLOP(T)])
		    END;
		  IF NARGS < PROCDEF:NUMARGS[P] THEN
		    BEGIN "not enough args"
			USERERR(1,1,"PARSER: NOT ENOUGH ARGMENTS FOR PROCEDURE");
			IF C = RNULL THEN C ← CELL:CDR[EXPRN:ARGS[E]];
			WHILE NARGS < PROCDEF:NUMARGS[P] DO
			  BEGIN
			    NARGS ← NARGS + 1;
			    CASE VARIABLE:DATATYPE[LLOP(T)] OF
			      BEGIN
		[SVAL_DTYPE]	C ← CELL:CDR[C] ← CONS(FALSEV,RNULL);
		[V3ECT_DTYPE]	C ← CELL:CDR[C] ← CONS(NILVECT,RNULL);
		[ROTN_DTYPE]	C ← CELL:CDR[C] ← CONS(NILROTN,RNULL);
		[TRANS_DTYPE]	C ← CELL:CDR[C] ← CONS(NILTRANS,RNULL);
		[FRAME_DTYPE]	C ← CELL:CDR[C] ← CONS(NILDEPROACH,RNULL);
		ELSE		C ← CELL:CDR[C] ← CONS(FALSEV,RNULL)
			      END
			  END
		    END "not enough args"
		END "procedure call";

[AREF_OP]	BEGIN "array reference"
		  NARGS ← 0;
		  WHILE EARGS ≠ RNULL DO
		    BEGIN "count args"
			NARGS ← NARGS + 1;
			VERIFY_DTYPE((C←LLOP(EARGS)),SVAL_DTYPE)
		    END;
		  IF NARGS < ARRAYDEF:NUMDIMS[P] THEN
		    BEGIN "not enough subscripts"
			USERERR(1,1,"PARSER: NOT ENOUGH SUBSCRIPTS");
			IF C = RNULL THEN C ← CELL:CDR[EXPRN:ARGS[E]];
			WHILE NARGS < ARRAYDEF:NUMDIMS[P] DO
			  BEGIN
			    NARGS ← NARGS + 1;
			    C ← CELL:CDR[C]
			     ← CONS(NEW_SVAL(ARRAYDEF:BDVALS[P][NARGS,0]),RNULL)
			  END
		    END "not enough subscripts"
		END "array reference";

[LAST_OP]	END;

	END;
!  asgbki, identlookup, ensym, vblmake, vtry;

RPTR(BLOCK) GVLBLK; ! Current block being gobbled;
RPTR(CMON) CCMON;  ! Current cmon being gobbled (if any);

INTEGER TEMP; INITIALIZE(TEMP←0);

INTEGER BLKNO; INITIALIZE(BLKNO←0);

PROCEDURE ASGBKI(RPTR(BLOCK) B);
    BEGIN
    BLKNO ← BLKNO + 1;
    BLOCK:BLID[B] ← "$B" & CVS(BLKNO)
    END;

RANY PROCEDURE IDENTLOOKUP(RPTR(IDENT) V);
    BEGIN
    RPTR(DEFID) D;
    IF RECTYPE(V) ≠ LOC(IDENT) THEN
	BEGIN
	USERERR(1,1,"DRYROT IN IDENTLOOKUP");
	RETURN(RNULL)
	END;
    D ← IDS;
    WHILE D ≠ RNULL ∧ ¬EQU(IDENT:ID[V],DEFID:NAME[D]) DO D ← DEFID:NEXT[D];
    IF D ≠ RNULL THEN RETURN (DEFID:VAL[D])	! Success - found it;
		 ELSE RETURN (V)		! Failure - not defined;
    END;

PROCEDURE ENSYM(RPTR(IDENT) ID; RANY V);
    BEGIN
    RANY D;
    IF RECTYPE(ID) ≠ LOC(IDENT) THEN
	BEGIN
	PRINT(CRLF&"****", IDENT:ID[ID], CRLF);
	USERERR(1,1,"NEED AN ID HERE");
	RETURN
	END;
    D ← IDENTLOOKUP(ID);
    IF RECTYPE(D) ≠ LOC(IDENT) ∧ VARIABLE:BLK[D] = GVLBLK THEN
	USERERR(1,1,"WARNING DUP ID: " & IDENT:ID[ID])
      ELSE
	BEGIN				! Add a new defid to the list;
	D ← NEW_RECORD(DEFID);
	DEFID:NAME[D] ← IDENT:ID[ID];
	DEFID:VAL[D] ← V;
	DEFID:NEXT[D] ← IDS;
	IDS ← D
	END
    END;

RPTR(VARIABLE,LBLVAR) PROCEDURE VBLMAKE(RPTR(IDENT) V; INTEGER DTYP);
    BEGIN
    RPTR(VARIABLE,LBLVAR) VV;
    IF DTYP = STMLAB_DTYPE ∨ DTYP = OMNLAB_DTYPE THEN
	VV ← NEW_LBL(IDENT:ID[V],DTYP,GVLBLK)
      ELSE
	VV ← NEW_VAR(IDENT:ID[V],DTYP,GVLBLK);
    ENSYM(V,VV);
    RETURN(VV)
    END;

RPTR(VARIABLE,LBLVAR) PROCEDURE VTRY(RANY V; INTEGER DTYP (INVALID_DTYPE));
    BEGIN  "vtry"
    ! Returns V.  If it was a declared variable, it
    checks its type to make sure it is DTYP (unless DTYP was not
    specified).  If it was not declared, VTRY declares it with DTYP.
    Complains if V is not a declared variable or an IDENT.;

    RVAR VAR;
    INTEGER RT,VDT;
    RT ← RECTYPE(V);
    IF RT = LOC(IDENT) THEN
	BEGIN
	V ← IDENTLOOKUP(V);
	RT ← RECTYPE(V)
	END;
    IF RT = LOC(IDENT) THEN
	BEGIN			! Must be declared;
	USERERR(1,1,"VTRY: Will define " & IDENT:ID[V]);
	VAR ← VBLMAKE(V,DTYP)
	END
    ELSE IF RT = LOC(ARRAYDEF) THEN RETURN(V)
    ELSE IF RT = LOC(PROCDEF) THEN RETURN(V)
    ELSE IF RT = LOC(VARIABLE) THEN VAR ← V
    ELSE IF RT = LOC(LBLVAR) THEN RETURN(V)
    ELSE BEGIN
	USERERR(1,1,"VTRY: Bad argument");
	RETURN(V)
	END;

    VDT ← VARIABLE:DATATYPE[VAR];
    IF (DTYP ≠ INVALID_DTYPE) ∧ (VDT ≠ DTYP) THEN
	BEGIN  ! May want to put right type in;
	IF VDT = INVALID_DTYPE	THEN VARIABLE:DATATYPE[VAR] ← DTYP
	ELSE IF VDT = FRAME_DTYPE ∧ DTYP=TRANS_DTYPE THEN BEGIN ! OK; END
	ELSE USERERR(1,1,"VTRY: " & VARIABLE:NAME[V] & " has wrong type")
	END;
    RETURN(VAR)
    END "vtry";
!  grovel (lllop, gllop, stgrovel, lgrovel, constelim);

INTERNAL RANY RECPROC GROVEL(RANY SE);
    BEGIN
    RCELL C;
    RANY KIND,V;
    INTEGER IX;
    OWN INTEGER REFFLG, VALFLG;  ! Used for reference & value decs;
    LABEL REGROVEL;

    RANY PROCEDURE LLLOP;
	RETURN(LLOP(C));

    RANY PROCEDURE GLLOP;
	IF C ≠ RNULL THEN RETURN(GROVEL(LLLOP)) ELSE RETURN(RNULL);

    RSTMNT PROCEDURE STGROVEL;
	IF C ≠ RNULL THEN
	    BEGIN
	    RANY S;
	    S ← GLLOP;
	    IF RECTYPE(S)=LOC(EXPRN) ∧ EXPRN:OP[S]=CALL_OP THEN S ← STMAKE(S);
	    RETURN(CHKREC(S,LOC(STMNT)))
	    END
	ELSE RETURN(STMAKE(RNULL));

    RCELL RECPROC LGROVEL(RCELL C);
	BEGIN  !  Grovels down a list;
	RCELL C1,C2,C3;
	C1 ← C3 ← RNULL;
	WHILE C ≠ RNULL DO
	    BEGIN
	    C2 ← GROVEL(LLOP(C));
	    IF C2 ≠ RNULL THEN
		BEGIN
		C2 ← CONS(C2,RNULL);
		IF C1 = RNULL THEN C1 ← C3 ← C2
			      ELSE CELL:CDR[C1] ← C2;
		C1 ← C2
		END
	    END;
	RETURN(C3)
	END;

RPTR (VALU$,EXPRN) PROCEDURE CONSTELIM (RPTR(EXPRN) EX);
    BEGIN "constelim"  ! Takes the expression EX and
    replaces it with a simpler one if possible.  At the moment, only
    checks one level deep, since it is called repeatedly at each level.
    It should be simple to make it recursive;
    INTEGER TYP, FLAG;
    RANY PTR;
    IF RECTYPE(EX) ≠ LOC(EXPRN) THEN
	BEGIN
	PRINT(CRLF&"****"); ALPRIN(EX);
	USERERR(1,1,"CONSTELIM:  Not an expression");
	RETURN(EX);
	END;
    !  Make sure the operands are all constants;
    PTR ← EXPRN:ARGS[EX];
    FLAG ← FALSE;
    WHILE PTR ≠ RNULL DO
	BEGIN "cloop"
	TYP ← RECTYPE(CELL:CAR[PTR]);
	IF FLAG ← (TYP=LOC(SVAL) ∨ TYP=LOC(V3ECT) ∨ TYP=LOC(ROTN) ∨ TYP=LOC(TRANS)
	    ∨ TYP=LOC(FRAME)) THEN PTR ← CELL:CDR[PTR]
			      ELSE DONE "cloop"
	END "cloop";
    IF ¬FLAG THEN RETURN(EX)  !  Can't do anything;
	     ELSE RETURN(EVALEXPR(EX,RNULL))
    END;
!  grovel: REGROVEL:  DIR, EOP, DTYP;

REGROVEL:
    IF RECTYPE(SE) ≠ LOC(CELL) THEN
	IF RECTYPE(SE) = LOC(IDENT) THEN RETURN(VTRY(SE)) ELSE RETURN(SE);
    KIND ← CELL:CAR[SE];
    C ← CELL:CDR[SE];

    IX ← RECTYPE(KIND);
    IF IX = LOC(IDENT) THEN
	BEGIN
	KIND ← IDENTLOOKUP(KIND);
	IX ← RECTYPE(KIND);
	END;
    IF IX = LOC(LBLVAR) THEN
	BEGIN
	V ← GROVEL(C);
	IX ← RECTYPE(V);
	IF LBLVAR:SEMANTICS[KIND] ≠ RNULL THEN
	    BEGIN
	    PRINT(CRLF&"****"); ALPRIN(KIND);
	    USERERR(1,1,"DUPLICATE USE OF LABEL")
	    END
	ELSE ASGLBL(KIND,V);
	RETURN(V)
	END
    ELSE IF IX ≠ LOC(RESWD) THEN RETURN(LGROVEL(SE));

    IX ← RESWD:TYPE[KIND];

    CASE IX OF
	    BEGIN

[DIR_CODE]	BEGIN		! DSKIN_OP is only directive;
		V ← GLLOP;
		IF RECTYPE(V) = LOC(STCONST) THEN
		    BEGIN
		    INTEGER CH;
		    CH ← READFILE(STCONST:VAL[V]);
		    IF CH < 0 THEN RETURN(RNULL);
		    INPLEV ← INPLEV+1;
		    SCNCHN[INPLEV] ← CH;
		    SCNSTK[INPLEV] ← INPUT(SCNCHN[INPLEV],LINBRK);
		    IF EQU(SCNSTK[INPLEV][1 FOR 9],"COMMENT ⊗") THEN
			BEGIN	! Skip over E directory page;
			DO SCNSTK[INPLEV] ← INPUT(SCNCHN[INPLEV],LINBRK)
			    UNTIL EQU(SCNSTK[INPLEV][1 FOR 3],"C⊗;")
				∨ EOF[SCNCHN[INPLEV]];
			IF EOF[SCNCHN[INPLEV]] THEN
			    USERERR(1,1,"DIRECTORY END NOT DETECTED");
			SCNSTK[INPLEV] ← NULL
			END;
		    SE ← READ;
		    GO TO REGROVEL
		    END
		END;

[EOP_CODE]	BEGIN	! Expression;
		V ← NEW_RECORD(EXPRN);
		EXPRN:OP[V] ← RESWD:CODE[KIND];
		EXPRN:ARGS[V] ← LGROVEL(C);
		DTYPE_CHECK(V);
		IF ¬(EXPRN:OP[V] = SSBRTN_OP ∧
		     SVAL:VAL[CELL:CAR[EXPRN:ARGS[V]]] = TIME_OP) THEN
		  V ← CONSTELIM(V);
		RETURN(V)
		END;

!  grovel: DTYP: ARRAY, PROCEDURE;

[DTYP_CODE]	BEGIN "VBL"
		IF RESWD:CODE[KIND] = REF_DTYPE THEN
		    BEGIN "refdec"
		    REFFLG ← TRUE;
		    GROVEL(C);
		    REFFLG ← FALSE
		    END
		ELSE IF RESWD:CODE[KIND] = VAL_DTYPE THEN
		    BEGIN "valdec"
		    VALFLG ← TRUE;
		    GROVEL(C);
		    VALFLG ← FALSE
		    END
		ELSE IF RESWD:CODE[KIND] = ARAY_DTYPE THEN
		    BEGIN "array dec"
		    INTEGER DT,NDIMS,I,J;
		    RPTR(ARRAYDEF) ARAY;
		    RCELL BNDS;
		    DT ← RESWD:CODE[LLLOP];
		    WHILE C ≠ RNULL DO
		      BEGIN
		      ARAY ← NEW_RECORD(ARRAYDEF);
		      ARRAYDEF:DATATYPE[ARAY] ← DT;
		      ARRAYDEF:BLK[ARAY] ← GVLBLK;
		      CONSON(ARAY,BLOCK:ARAYS[GVLBLK]);
		      V ← LLLOP; ! fetch array name;
		      ARRAYDEF:NAME[ARAY] ← IDENT:ID[V];
		      ENSYM(V,ARAY);
		      BNDS ← CELL:CAR[C];
		      NDIMS ← 0;
		      WHILE BNDS ≠ RNULL DO
			BEGIN
			NDIMS ← NDIMS + 1;
			BNDS ← CELL:CDR[CELL:CDR[BNDS]]
			END;
		      ARRAYDEF:NUMDIMS[ARAY] ← NDIMS;
		      IF NDIMS THEN
			BEGIN ! this is so procedure arguments can be arrays;
			NewArray(REXPR,ARRAYDEF:BOUNDS[ARAY],[1:NDIMS,0:3]);
			NewArray(INTEGER,ARRAYDEF:BDVALS[ARAY],[1:NDIMS,0:2]);
			END;
		      BNDS ← LLLOP;
		      FOR I ← 1 TIL NDIMS DO
			FOR J ← 0 TIL 1 DO
			  BEGIN
			  ARRAYDEF:BOUNDS[ARAY][I,J] ← GROVEL(LLOP(BNDS));
			  IF RECTYPE(ARRAYDEF:BOUNDS[ARAY][I,J]) = LOC(EXPRN) THEN
			    ARRAYDEF:BOUNDS[ARAY][I,J+2] ←
			      NEW_VAR(NULL,SVAL_DTYPE,BLOCK:PARENT[GVLBLK])
			  END
		      END
		    END "array dec"
		ELSE IF RESWD:CODE[KIND] = PROC_DTYPE THEN
		    BEGIN "procedure dec"
		    INTEGER NARGS;
		    RPTR(BLOCK) SAVEBLK,T;
		    RPTR(DEFID) BLKIDS;
		    RANY P,N;
		    RCELL ARGLIST,L;
		    V ← NEW_RECORD(PROCDEF);
		    PROCDEF:DATATYPE[V] ← (IF RECTYPE(CELL:CAR[C]) =
		      LOC(RESWD) THEN RESWD:CODE[LLLOP] ELSE 0);
		    CONSON(V,BLOCK:PROCS[GVLBLK]);
		    P ← LLLOP; ! get procedure's name;
		    PROCDEF:NAME[V] ← IDENT:ID[P];
		    ENSYM(P,V);
		    PROCDEF:BLK[V] ← GVLBLK;
		    BLKIDS ← IDS;
		    T ← NEW_RECORD(BLOCK);
		    PROCDEF:BODY[V] ← STMAKE(T);
		    ASGBKI(T);
		    BLOCK:PARENT[T] ← SAVEBLK ← GVLBLK;
		    GVLBLK ← T;
		    L ← RNULL;
		    ARGLIST ← CELL:CAR[C]; ! save pointer to arg list;
		    LGROVEL(LLLOP); ! parse the arg list defining variables;
		    WHILE ARGLIST ≠ RNULL DO
		      BEGIN
		      P ← LLOP(ARGLIST);
		      WHILE P ≠ RNULL DO
			IF RECTYPE((N←LLOP(P))) = LOC(IDENT) THEN
			  BEGIN
			  NARGS ← NARGS + 1;
			  N ← CONS(IDENTLOOKUP(N),RNULL);
			  IF L = RNULL THEN PROCDEF:ARGS[V] ← N
				       ELSE CELL:CDR[L] ← N;
			  L ← N
			  END
		      END;
		    PROCDEF:NUMARGS[V] ← NARGS;
		    BLOCK:CODE[T] ← LGROVEL(C); ! parse procedure body;
		    IDS ← BLKIDS;	! Pop variables for this block;
		    GVLBLK ← SAVEBLK
		    END "procedure dec"
		ELSE WHILE C ≠ RNULL DO
		    BEGIN
		    V ← LLLOP;
		    IF RECTYPE(V) ≠ LOC(IDENT) THEN
			BEGIN
			PRINT(CRLF&"****"); RECPRN(V); PRINT(CRLF);
			USERERR(1,1,"FUNNY THING FOR VARIABLE");
			CONTINUE
			END;
		    V ← VBLMAKE(V,RESWD:CODE[KIND]);
		    IX ← IF REFFLG THEN REFARG ELSE IF VALFLG THEN VALARG ELSE 0;
		    VARIABLE:ATTRIBUTES[V] ← VARIABLE:ATTRIBUTES[V] LOR IX
		    END;
		RETURN(RNULL)
		END;

!  grovel: main body:	PROG,BLOCK,COBLOCK,FORR,WHIL,IFF,PAUSE,ABORT,COMMNT;

[RW_CODE]	BEGIN "RWCODE"
		CASE RESWD:CODE[KIND] OF
		    BEGIN

    [PROGTYPE]	    BEGIN
		    V←NEW_RECORD(PROG);
		    PROG:CODE[V]←STGROVEL;
		    RETURN(STMAKE(V))
		    END;

    [BLOCKTYPE]     BEGIN
		    RBLK SAVEBLK;
		    RPTR(DEFID) BLKIDS;
		    V ← NEW_RECORD(BLOCK);
		    BLKIDS ← IDS;
		    ASGBKI(V);
		    SAVEBLK ← GVLBLK;
		    BLOCK:PARENT[V] ← SAVEBLK;
		    GVLBLK ← V;
		    BLOCK:CODE[V] ← LGROVEL(C);
		    IDS ← BLKIDS;	! Pop variables for this block;
		    GVLBLK ← SAVEBLK;
		    RETURN(STMAKE(V))
		    END;

    [COBLOCKTYPE]   BEGIN
		    V ← NEW_RECORD(COBLOCK);
		    COBLOCK:CODE[V] ← LGROVEL(C);
		    RETURN(STMAKE(V))
		    END;

    [FORRTYPE]	    BEGIN
		    V ← NEW_RECORD(FORR);
		    FORR:CONVAR[V] ← (IF RECTYPE(CELL:CAR[C])=LOC(IDENT)
		      THEN VTRY(LLLOP,SVAL_DTYPE) ELSE GLLOP);
		    FORR:INITIAL[V] ← GLLOP;
		    FORR:STEP[V] ← GLLOP;
		    FORR:FINAL[V] ← GLLOP;
		    FORR:BODY[V] ← STGROVEL;
		    RETURN(STMAKE(V))
		    END;

    [WHILTYPE]	    BEGIN
		    V ← NEW_RECORD(WHIL);
		    WHIL:COND[V] ← GLLOP;
		    WHIL:BODY[V] ← STGROVEL;
		    RETURN(STMAKE(V))
		    END;

    [UNTLTYPE]	    BEGIN
		    V ← NEW_RECORD(UNTL);
		    UNTL:BODY[V] ← STGROVEL;
		    UNTL:COND[V] ← GLLOP;
		    RETURN(STMAKE(V))
		    END;

    [IFFTYPE]	    BEGIN
		    V ← NEW_RECORD(IFF);
		    IFF:COND[V] ← GLLOP;
		    IFF:THN[V] ← STGROVEL;
		    IFF:ELS[V] ← STGROVEL;
		    RETURN(STMAKE(V))
		    END;

    [PAUSETYPE]     BEGIN
		    V ← NEW_RECORD(PAUSE);
		    PAUSE:VAL[V] ← GLLOP;
		    RETURN(STMAKE(V))
		    END;

    [PROMPTTYPE]    BEGIN
		    V ← NEW_RECORD(PROMPT);
		    PROMPT:VAL[V] ← LGROVEL(C); ! Gets a list of print items;
		    RETURN(STMAKE(V))
		    END;

    [ABORTTYPE]     BEGIN
		    V ← NEW_RECORD(ABORT);
		    ABORT:VAL[V] ← LGROVEL(C);	! Gets a list of print items;
		    RETURN(STMAKE(V))
		    END;

    [COMMNTTYPE]    BEGIN  ! Coded by RF;
		    V ← NEW_RECORD(COMMNT);
		    !  COMMNT:HESAYS[V] ← LGROVEL(C);
			! You don't really want to keep that junk;
		    RETURN(STMAKE(V))
		    END;

!  grovel: main body:	CASE, RETURN;

    [KASETYPE]	    BEGIN
		    RANY F;
		    RCELL T,B;
		    INTEGER S,I,N,J;
		    V ← NEW_RECORD(KASE);
		    S ← I ← N ← 0;
		    KASE:INDEX[V] ← GLLOP;
		    IF RECTYPE(CELL:CAR[C]) = LOC(CELL) THEN
		      BEGIN "regular case statement"
		      T ← C;
		      WHILE T ≠ RNULL DO    ! count the statements;
			BEGIN LLOP(T); N ← N +1 END;
		      KASE:RANGE[V] ← N;
		      NewArray(INTEGER,KASE:LABS[V],[0:N,0:1]);
		      ARRCLR(KASE:LABS[V],N);
		      FOR I ← 0 TIL N-1 DO
			IF (F←LLLOP) = RNULL THEN KASE:LABS[V][I,0] ← N ELSE
			  BEGIN
			  KASE:LABS[V][I,0] ← S;
			  S ← S + 1;
			  F ← GROVEL(F);
			  IF RECTYPE(F)=LOC(EXPRN) ∧ EXPRN:OP[F]=CALL_OP THEN
				    F←STMAKE(F);
			  F ← CONS(F,RNULL);
			  IF T = RNULL THEN KASE:STMNTS[V] ← F
				       ELSE CELL:CDR[T] ← F;
			  T ← F
			  END
		      END "regular case statement"
		    ELSE
		      BEGIN "numbered case statement"
		      T ← C;
		      WHILE T ≠ RNULL DO ! establish the range of the index;
			IF RECTYPE(F←LLOP(T)) = LOC(SVAL) THEN
			  N ← N MAX (I←SVAL:VAL[F]);
		      KASE:RANGE[V] ← N ← N + 1;
		      NewArray(INTEGER,KASE:LABS[V],[0:N,0:1]);
		      ARRCLR(KASE:LABS[V],N);
		      B ← C; I ← 0;
		      WHILE C ≠ RNULL DO
			IF (F←LLLOP) = RNULL THEN BEGIN "whoops"
			  WHILE B≠C DO IF RECTYPE(F←LLOP(B))=LOC(SVAL) THEN
			    KASE:LABS[V][SVAL:VAL[F],0] ← N END "whoops"
			ELSE IF RECTYPE(F) = LOC(SVAL) THEN
			  IF SVAL:VAL[F] ≥ 0 THEN
			    KASE:LABS[V][SVAL:VAL[F],0] ← S
			  ELSE
			    BEGIN
			    FOR J ← 0 TIL N DO
			      IF KASE:LABS[V][J,0] = N THEN
				KASE:LABS[V][J,0] ← S;
			    KASE:RANGE[V] ← - KASE:RANGE[V]
			    END
			ELSE
			  BEGIN
			  B ← C; S ← S + 1;
			  F ← GROVEL(F);
			  IF RECTYPE(F)=LOC(EXPRN) ∧ EXPRN:OP[F]=CALL_OP THEN
				    F←STMAKE(F);
			  F ← CONS(F,RNULL);
			  IF T = RNULL THEN KASE:STMNTS[V] ← F
				       ELSE CELL:CDR[T] ← F;
			  T ← F
			  END
		      END "numbered case statement";
		    KASE:NSTMNTS[V] ← S;
		    IF KASE:RANGE[V] ≥ 0 THEN KASE:LABS[V][N,0] ← S;
		    RETURN(STMAKE(V))
		    END;

    [RETRNTYPE]     BEGIN
		    V ← NEW_RECORD(RETRN);
		    RETRN:VAL[V] ← GLLOP;
		    RETURN(STMAKE(V))
		    END;

!  grovel: main body:	DEPROACH, PAS, PVL, NOTE, NOTE1, NOTE2;

    [DEPROACHTYPE]  BEGIN
		    V ← NEW_RECORD(DEPROACH);
		    DEPROACH:VAR[V] ← GLLOP;
		    DEPROACH:VAL[V] ← GLLOP;
		    RETURN(STMAKE(V))
		    END;

    [PASTYPE]	    BEGIN
		    V ← NEW_RECORD(PAS);
		    PAS:VAR[V] ← (IF RECTYPE(CELL:CAR[C])=LOC(IDENT)
			THEN LLLOP ELSE GLLOP);
		    PAS:VAL[V] ← GLLOP;
		    IF RECTYPE(PAS:VAR[V]) = LOC(IDENT) THEN
		      PAS:VAR[V] ← VTRY(PAS:VAR[V],GET_DTYPE(PAS:VAL[V]));
		    RETURN(STMAKE(V))
		    END;

    [PVLTYPE]	    BEGIN
		    V ← NEW_RECORD(PVL);
		    PVL:VL[V] ← LGROVEL(C);
		    RETURN(V)
		    END;

    [NOTETYPE]	    BEGIN
		    V ← NEW_RECORD(NOTE);
		    NOTE:HESAYS[V] ← GLLOP;   ! Better be a string constant;
		    RETURN(V)
		    END;

    [NOTE1TYPE]     BEGIN
		    V ← NEW_RECORD(NOTE1);
		    NOTE1:HESAYS[V] ← GLLOP;  ! Better be a string constant;
		    RETURN(V)
		    END;

    [NOTE2TYPE]     BEGIN
		    V ← NEW_RECORD(NOTE2);
		    NOTE2:HESAYS[V] ← GLLOP;  ! Better be a string constant;
		    RETURN(V)
		    END;

    [DEBUGTYPE]     BEGIN
		    PRINT(STCONST:VAL[GLLOP],CRLF); ! Better be a string constant;
		    RETURN(RNULL)
		    END;

!  grovel: main body:	AFFIX, UNFIX;

    [AFFIXTYPE]     BEGIN
		    RPTR(VARIABLE) VAR;
		    V←NEW_RECORD(AFFIX);
		    AFFIX:FRAME1[V] ← (IF RECTYPE(CELL:CAR[C])=LOC(IDENT)
			THEN VTRY(LLLOP,FRAME_DTYPE) ELSE GLLOP);
		    AFFIX:FRAME2[V] ← (IF RECTYPE(CELL:CAR[C])=LOC(IDENT)
			THEN VTRY(LLLOP,FRAME_DTYPE) ELSE GLLOP);
		    AFFIX:BYVAR[V] ← (IF RECTYPE(CELL:CAR[C])=LOC(IDENT)
			THEN VTRY(LLLOP,TRANS_DTYPE) ELSE GLLOP);
		    IF AFFIX:BYVAR[V] = RNULL THEN
			BEGIN
			AFFIX:BYVAR[V] ← VAR ← NEW_RECORD(VARIABLE);
			VARIABLE:NAME[VAR] ← NULL;
			VARIABLE:DATATYPE[VAR] ← TRANS_DTYPE;
			VARIABLE:BLK[VAR] ← GVLBLK
			END;
		    AFFIX:ATEXP[V] ← GLLOP;
		    AFFIX:RIGID[V] ←	! Rigid (=TRUE) is default;
			C = RNULL ∨ ¬EQU("NONRIGIDLY",IDENT:ID[LLOP(C)]);
		    RETURN(STMAKE(V))
		    END;

    [UNFIXTYPE]     BEGIN
		    V←NEW_RECORD(UNFIX);
		    UNFIX:FRAME1[V] ← (IF RECTYPE(CELL:CAR[C])=LOC(IDENT)
			THEN VTRY(LLLOP,FRAME_DTYPE) ELSE GLLOP);
		    UNFIX:FRAME2[V] ← (IF RECTYPE(CELL:CAR[C])=LOC(IDENT)
			THEN VTRY(LLLOP,FRAME_DTYPE) ELSE GLLOP);
		    RETURN(STMAKE(V))
		    END;

!  grovel: main body:	V3ECT, TRANS, ASSIGNMENT, EVDO, PRNT, CMABLE;

    [V3ECTTYPE]     BEGIN
		    V ← NEW_RECORD(V3ECT);
		    V3ECT:X[V] ← SVAL:VAL[LLLOP];
		    V3ECT:Y[V] ← SVAL:VAL[LLLOP];
		    V3ECT:Z[V] ← SVAL:VAL[LLLOP];
		    RETURN(V)
		    END;

    [TRANSTYPE]     BEGIN
		    V ← NEW_RECORD(TRANS);
		    TRANS:R[V] ← GLLOP;
		    TRANS:P[V] ← GLLOP;
		    RETURN(V)
		    END;

    [PRNTTYPE]	    BEGIN "prnt"
		    V ← NEW_RECORD(PRNT);
		    PRNT:VAL[V] ← LGROVEL(C); ! Gets a list of print items;
		    RETURN(STMAKE(V))
		    END "prnt";

   [ASSIGNMENTTYPE] BEGIN  "assign"
		    V ← NEW_RECORD(ASSIGNMENT);
		    ASSIGNMENT:VAR[V] ← (IF RECTYPE(CELL:CAR[C])=LOC(IDENT)
			THEN LLLOP ELSE GLLOP);
		    ASSIGNMENT:VAL[V] ← GLLOP;
		    IF RECTYPE(ASSIGNMENT:VAR[V]) = LOC(IDENT) THEN
		      ASSIGNMENT:VAR[V] ←
			VTRY(ASSIGNMENT:VAR[V],GET_DTYPE(ASSIGNMENT:VAL[V]));
		    RETURN(STMAKE(V))
		    END "assign";

    [EVDOTYPE]	    BEGIN
		    !  e.g.: (EV EVAR1 +) will signal the event;
		    V ← NEW_RECORD(EVDO);
		    EVDO:VAR[V] ← (IF RECTYPE(CELL:CAR[C])=LOC(IDENT)
		      THEN VTRY(LLLOP,EVENT_DTYPE) ELSE GLLOP);
		    IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    IF IX = "+" THEN EVDO:OP[V] ← 0
		    ELSE IF IX = "-" THEN EVDO:OP[V] ← 1
		    ELSE USERERR(1,1,"What kind of EV is " & IX & "?");
		    RETURN(STMAKE(V))
		    END;

    [CMABLETYPE]    BEGIN
		    !  e.g.: (CMABLE + cmon) will enable the cmon;
		    V ← NEW_RECORD(CMABLE);
		    IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    IF IX = "+" THEN CMABLE:FLAG[V] ← 0
		    ELSE IF IX = "-" THEN CMABLE:FLAG[V] ← 1
		    ELSE USERERR(1,1,"What kind of CMABLE is " & IX & "?");
			    ! Get the cmon's label;
		    IF C ≠ RNULL THEN	    ! refers to labelled cmon;
			CMABLE:WHAT[V] ← VTRY(LLLOP,OMNLAB_DTYPE)
		    ELSE		    ! refers to unlabelled cmon;
			IF IX="-" THEN USERERR(1,1,"Cmon can't disable itself.")
		    ELSE
			IF CCMON ≠ RNULL THEN CMABLE:WHAT[V] ← CCMON
			ELSE USERERR(1,1,"Must specify name of cmon.");
		    RETURN(STMAKE(V))
		    END;

!  grovel: main body:   MOVE$, OPERATE, CENTER, STOP, motion clauses;

    [MOVE$TYPE]     BEGIN  "move$"
		    RANY P;
		    V ← NEW_RECORD(MOVE$);
		    MOVE$:WHAT[V] ← GLLOP;
		    MOVE$:DEST[V] ← GLLOP;
		    MOVE$:DEXP[V] ← NEW_RECORD(DEXPR);
			! Can expect VIA, DURATION, CMON, DEPROACHES;
		    MOVE$:CLAUSES[V] ← LGROVEL(C);
		    P←MOVE$:CLAUSES[V];
		    WHILE P ≠ RNULL DO      ! All this does is turn CMON & S_FAC;
			BEGIN               ! statements into regular clauses;
			IF RECTYPE(CELL:CAR[P])=LOC(STMNT) THEN
			    CELL:CAR[P]←STMNT:SEMANTICS[CELL:CAR[P]];
			IF RECTYPE(CELL:CAR[P])=LOC(DEXPR) THEN
			    MOVE$:DEST[V] ← DEXPR:EXPN[CELL:CAR[P]];
			P←CELL:CDR[P];
			END;
		    RETURN(STMAKE(V))
		    END "move$";

    [OPERATETYPE]   BEGIN  "operate"
		    RANY P;
		    V ← NEW_RECORD(OPERATE);
		    OPERATE:WHAT[V] ← GLLOP;
		    OPERATE:DEST[V] ← (IF RECTYPE(CELL:CAR[C])=LOC(CHAR_REC)
					THEN LLLOP ELSE GLLOP);
			! Can expect DURATION, CMON, TORQUE, VELOCITY,
					STOP_WAIT_TIME, ... ;
		    OPERATE:CLAUSES[V] ← LGROVEL(C);
		    P←MOVE$:CLAUSES[V];
		    WHILE P ≠ RNULL DO      ! All this does is turn CMON & S_FAC;
			BEGIN               ! statements into regular clauses;
			IF RECTYPE(CELL:CAR[P])=LOC(STMNT) THEN
			    CELL:CAR[P]←STMNT:SEMANTICS[CELL:CAR[P]];
			P←CELL:CDR[P]
			END;
		    RETURN(STMAKE(V))
		    END "operate";

    [CENTERTYPE]    BEGIN  "center"
		    RANY P;
		    V ← NEW_RECORD(CENTER);
		    CENTER:CF[V] ← GLLOP;
			! Can expect CMON someday, ERROR handler now;
		    CENTER:CLAUSES[V] ← LGROVEL(C);
		    P←CENTER:CLAUSES[V];
		    WHILE P ≠ RNULL DO      ! All this does is turn CMON & S_FAC;
			BEGIN               ! statements into regular clauses;
			IF RECTYPE(CELL:CAR[P])=LOC(STMNT) THEN
			    CELL:CAR[P]←STMNT:SEMANTICS[CELL:CAR[P]];
			P←CELL:CDR[P]
			END;
		    RETURN(STMAKE(V))
		    END "center";

    [ERRORTYPE]     BEGIN "error"
		    V ← NEW_RECORD(ERROR);
		    ERROR:BITS[V] ← GLLOP;
		    ERROR:BODY[V] ← STGROVEL;
		    RETURN(V)
		    END "error";

    [RETRYTYPE]     BEGIN "retry"
		    V ← NEW_RECORD(RETRY);
		    RETURN(STMAKE(V))
		    END "retry";

    [STOPTYPE]      BEGIN "stop"
		    V ← NEW_RECORD(STOP);
		    STOP:CF[V] ← GLLOP;
		    RETURN(STMAKE(V))
		    END "stop";

    [CMONTYPE]      BEGIN
		    RPTR(CMON) S;
		    S ← CCMON;              ! save outermost cmon;
		    CCMON ← V ← NEW_RECORD(CMON);
		    IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    IF IX = "+" THEN CMON:FLAGS[V] ← 0         ! Regular cmon;
		       ELSE IF IX = "-" THEN CMON:FLAGS[V] ← 1 ! Deferred cmon;
		       ELSE USERERR(1,1,"What kind of CMON is " & IX & "?");
		    CMON:CONDITION[V] ← GLLOP;
		    CMON:CONCLUSION[V] ← STGROVEL;
		    CCMON ← S;              ! restore old outermost cmon;
		    IF RECTYPE(CMON:CONDITION[V]) = LOC(ERROR) THEN
			BEGIN	! treat error handler specially;
			ERROR:BODY[CMON:CONDITION[V]] ← CMON:CONCLUSION[V];
			RETURN(CMON:CONDITION[V]);
			END;
		    CONSON(V,BLOCK:CMONS[GVLBLK]);
		    RETURN(STMAKE(V))
		    END;

    [VIATYPE]       BEGIN "via"
		    RANY CLS;  ! Clause;
		    V ← NEW_RECORD(VIA);
		    VIA:PLACE[V] ← GLLOP;
		    VERIFY_DTYPE(VIA:PLACE[V],TRANS_DTYPE); ! Check type is ok;
		    VIA:ACTPLACE[V] ← NEW_RECORD(DEXPR);
		    WHILE C ≠ RNULL DO
			BEGIN
			IF RECTYPE(CLS←GLLOP) = LOC(VELOCITY) THEN
			    VIA:VELOC[V] ← CLS
			ELSE IF RECTYPE(CLS) = LOC(DURATION) THEN
			    VIA:TIME[V] ← CLS
			ELSE IF RECTYPE(CLS) = LOC(STMNT) THEN
			    IF RECTYPE(STMNT:SEMANTICS[CLS]) = LOC(EVDO)
				∧ EVDO:OP[STMNT:SEMANTICS[CLS]]=0
			      THEN	! Treat SIGNAL as special;
				VIA:CODE[V] ← STMNT:SEMANTICS[CLS]
			      ELSE
				BEGIN
				RPTR(CMON) S;
				VIA:CODE[V] ← S ← NEW_RECORD(CMON);
				CMON:CONDITION[S] ←
				  NEW_VAR(".E"&CVS(TEMP←TEMP+1),EVENT_DTYPE,GVLBLK);
				CMON:CONCLUSION[S] ← CLS;
				CONSON(S,BLOCK:CMONS[GVLBLK]);
				END
			ELSE BEGIN ALPRIN(CLS);PRINT(CRLF);
			    USERERR(1,1,"Funny thing for VIA clause") END;
			END;
		    RETURN(V)
		    END "via";

    [APPROACHTYPE]  BEGIN "approach" 
		    RANY CLS;  ! Clause for code;
		    V ← NEW_RECORD(APPROACH);
		    APPROACH:THRU[V] ← GLLOP;
		    APPROACH:ACTPLACE[V] ← NEW_RECORD(DEXPR);
		    CLS ← GLLOP;
		    IF CLS ≠ RNULL THEN		! Deal with associated code;
		     IF RECTYPE(STMNT:SEMANTICS[CLS]) = LOC(EVDO)
			∧ EVDO:OP[STMNT:SEMANTICS[CLS]]=0
		      THEN      ! Treat SIGNAL as special;
			APPROACH:CODE[V] ← STMNT:SEMANTICS[CLS]
		      ELSE
			BEGIN
			RPTR(CMON) S;
			APPROACH:CODE[V] ← S ← NEW_RECORD(CMON);
			CMON:CONDITION[S] ←
			  NEW_VAR(".E"&CVS(TEMP←TEMP+1),EVENT_DTYPE,GVLBLK);
			CMON:CONCLUSION[S] ← CLS;
			CONSON(S,BLOCK:CMONS[GVLBLK]);
			END;
		    RETURN(V)
		    END "approach";

    [DEPARTURETYPE] BEGIN "departure"
		    RANY CLS;  ! Clause for code;
		    V ← NEW_RECORD(DEPARTURE);
		    DEPARTURE:THRU[V] ← GLLOP;
		    DEPARTURE:ACTPLACE[V] ← NEW_RECORD(DEXPR);
		    CLS ← GLLOP;
		    IF CLS ≠ RNULL THEN		! Deal with associated code;
		     IF RECTYPE(STMNT:SEMANTICS[CLS]) = LOC(EVDO)
			∧ EVDO:OP[STMNT:SEMANTICS[CLS]]=0
		      THEN      ! Treat SIGNAL as special;
			DEPARTURE:CODE[V] ← STMNT:SEMANTICS[CLS]
		      ELSE
			BEGIN
			RPTR(CMON) S;
			DEPARTURE:CODE[V] ← S ← NEW_RECORD(CMON);
			CMON:CONDITION[S] ←
			  NEW_VAR(".E"&CVS(TEMP←TEMP+1),EVENT_DTYPE,GVLBLK);
			CMON:CONCLUSION[S] ← CLS;
			CONSON(S,BLOCK:CMONS[GVLBLK]);
			END;
		    RETURN(V)
		    END "departure";

    [WOBBLETYPE]    BEGIN "wobble"
		    V ← NEW_RECORD(WOBBLE);
		    WOBBLE:VAL[V] ← GLLOP;
		    RETURN(V)
		    END "wobble";

    [OPENINGTYPE]   BEGIN "opening"
		    V ← NEW_RECORD(OPENING);
		    OPENING:VAL[V] ← GLLOP;
		    RETURN(V)
		    END "opening";

    [DURATIONTYPE]  BEGIN "duration"
		    V ← NEW_RECORD(DURATION);
		    IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    DURATION:TIME_RELN[V] ←
			IF IX = ">" THEN 1
			ELSE IF IX = "<" THEN 2
			ELSE IF IX = "=" THEN 3
			ELSE 0;
		    DURATION:TIME[V] ← GLLOP;
		    RETURN(V)
		    END "duration";

    [VELOCITYTYPE]  BEGIN "velocity"
		    V ← NEW_RECORD(VELOCITY);
		    VELOCITY:VELOC[V] ← GLLOP;
		    RETURN(V)
		    END "velocity";

    [FORCETYPE]     BEGIN "force"
		    V ← NEW_RECORD(FORCE);
		    FORCE:DIRECT[V] ← GLLOP;
		    IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    FORCE:REL[V] ← IF IX = "<" THEN SIGLT ELSE SIGGE;
			    ! treat "=" & "≥" the same;
		    IF RECTYPE(CELL:CAR[C]) = LOC(CHAR_REC) THEN
			IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    IF IX = "+" THEN FORCE:REL[V] ← FORCE:REL[V] LOR SIGMAG;
		    FORCE:VAL[V] ← GLLOP;
		    IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    FORCE:TYPE[V] ← IF IX = "-" THEN FALSE ELSE TRUE;
			    ! force along axis = TRUE, torque about axis = FALSE;
		    FORCE:F_F[V] ← GLLOP; ! Get force frame spec;
		    RETURN(V)
		    END "force";

    [STIFFTYPE]     BEGIN "stiffness"
		    V ← NEW_RECORD(STIFF);
!		    STIFF:STIFFNESS[V] ← LGROVEL(LLLOP); ! Get the 6 stiffness values;
		    STIFF:K[V] ← GLLOP; ! Get the 3 force values;
		    STIFF:G[V] ← GLLOP; ! Get the 3 torque values;
		    STIFF:F_F[V] ← GLLOP; ! Get force frame spec;
		    RETURN(V)
		    END "stiffness";

    [GATHERTYPE]    BEGIN "gather"
		    V ← NEW_RECORD(GATHER);
		    IX ← 0;
		    WHILE C ≠ RNULL DO	! See what forces we're to gather;
			BEGIN
			STRING S;
			S ← IDENT:ID[CELL:CAR[C]];
			IX ← IX LOR
			 (IF EQU(S,"TBL") THEN 1 LSH 12 ELSE
			  IF S = "F" THEN 1 LSH (S[2 TO 2] - "X") ELSE
			  IF S = "M" THEN 1 LSH (S[2 TO 2] - "X" + 3) ELSE
			  IF S = "T" THEN 1 LSH (S[2 TO 2] - "1" + 6) ELSE 0);
			LLOP(C)
			END;
		    GATHER:BITS[V] ← IX; ! Store away forces to gather;
		    RETURN(V)
		    END "gather";

    [F_FRAMETYPE]   BEGIN "force frame"
		    V ← NEW_RECORD(F_FRAME);
		    F_FRAME:FRAME[V] ← GLLOP;
		    IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    F_FRAME:C_SYS[V] ← IF IX = "⊗" THEN FHAND ELSE FTABLE;
		    RETURN(V)
		    END "force frame";

    [SETBASETYPE]   BEGIN "setbase" ! This and WRIST below are temp hacks;
		    V ← NEW_RECORD(SETBASE);
		    IF C ≠ NULL_RECORD THEN
			IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    SETBASE:VAL[V] ← IF IX = "-" THEN FALSE ELSE TRUE;
		    RETURN(STMAKE(V))
		    END "setbase";

    [WRISTTYPE]     BEGIN "wrist"
		    V ← NEW_RECORD(WRIST);
		    WRIST:K[V] ← GLLOP;
		    WRIST:G[V] ← GLLOP;
		    RETURN(STMAKE(V))
		    END "wrist";

    [S_FACTYPE]     BEGIN "speed_factor"
		    V ← NEW_RECORD(S_FAC);
		    S_FAC:VAL[V] ← GLLOP;
		    RETURN(STMAKE(V))
		    END "speed_factor";

    [NNULLTYPE]     BEGIN "nnull"
		    V ← NEW_RECORD(NNULL);
		    IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    NNULL:FLAG[V] ← IF IX = "+" THEN TRUE ELSE FALSE;
		    RETURN(V)
		    END "nnull";

    [RTMOVETYPE]    BEGIN "rtmove"	! Use runtime traj calc - hack for msm;
		    V ← NEW_RECORD(RTMOVE);
		    RETURN(V)
		    END "rtmove";

    [SW_TIMETYPE]   BEGIN "stop_wait_time"
		    V ← NEW_RECORD(SW_TIME);
		    SW_TIME:VAL[V] ← GLLOP;
		    RETURN(STMAKE(V))
		    END "stop_wait_time";

    [CWTYPE]	    BEGIN
		    V ← NEW_RECORD(CW);
		    IX ← CHAR_REC:CHAR[CHKREC(LLLOP,LOC(CHAR_REC))];
		    CW:FLAG[V] ← IF IX = "-" THEN TRUE ELSE FALSE;
		    RETURN(STMAKE(V))
		    END;

    [TOTYPE]	    BEGIN "to"	! Kludge for alternative MOVE syntax;
		    V ← NEW_RECORD(DEXPR);
		    DEXPR:EXPN[V] ← GLLOP;	! Get destination for MOVE;
		    RETURN(V)
		    END "to";

    ELSE            RETURN(RNULL)

		    END

	    END;

  ELSE	 END;

    RETURN(SE)
    END;

END $$PRGID;